package great.project.config;

import great.project.constant.ResultEnum;
import great.project.dto.ResponseResult;
import great.project.exception.ParamErrorException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

	/**
	 * 忽略参数异常处理器
	 *
	 * @param e 忽略参数异常
	 * @return ResponseResult
	 */
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ExceptionHandler(MissingServletRequestParameterException.class)
	public ResponseResult parameterMissingExceptionHandler(MissingServletRequestParameterException e) {
		log.error("", e);
		return new ResponseResult(ResultEnum.PARAMETER_ERROR.getCode(), "请求参数 " + e.getParameterName() + " 不能为空");
	}

	/**
	 * 缺少请求体异常处理器
	 *
	 * @param e 缺少请求体异常
	 * @return ResponseResult
	 */
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ExceptionHandler(HttpMessageNotReadableException.class)
	public ResponseResult parameterBodyMissingExceptionHandler(HttpMessageNotReadableException e) {
		log.error("", e);
		return new ResponseResult(ResultEnum.PARAMETER_ERROR.getCode(), "请求参数格式有误");
	}

	/**
	 * 参数效验异常处理器
	 *
	 * @param e 参数验证异常
	 * @return ResponseInfo
	 */
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public ResponseResult parameterExceptionHandler(MethodArgumentNotValidException e) {
		log.error("", e);
		// 获取异常信息
		BindingResult exceptions = e.getBindingResult();
		// 判断异常中是否有错误信息，如果存在就使用异常中的消息，否则使用默认消息
		if (exceptions.hasErrors()) {
			List<ObjectError> errors = exceptions.getAllErrors();
			if (!errors.isEmpty()) {
				// 这里列出了全部错误参数，按正常逻辑，只需要第一条错误即可
				FieldError fieldError = (FieldError) errors.get(0);
				return new ResponseResult(ResultEnum.PARAMETER_ERROR.getCode(), fieldError.getDefaultMessage());
			}
		}
		return new ResponseResult(ResultEnum.PARAMETER_ERROR);
	}

	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ExceptionHandler(BindException.class)
	public ResponseResult parameterBindExceptionHandler(BindException e) {
		log.error("", e);
		// 获取异常信息
		BindingResult exceptions = e.getBindingResult();
		// 判断异常中是否有错误信息，如果存在就使用异常中的消息，否则使用默认消息
		if (exceptions.hasErrors()) {
			List<ObjectError> errors = exceptions.getAllErrors();
			if (!errors.isEmpty()) {
				// 这里列出了全部错误参数，按正常逻辑，只需要第一条错误即可
				FieldError fieldError = (FieldError) errors.get(0);
				return new ResponseResult(ResultEnum.PARAMETER_ERROR.getCode(), fieldError.getDefaultMessage());
			}
		}
		return new ResponseResult(ResultEnum.PARAMETER_ERROR);
	}

	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ExceptionHandler(ConstraintViolationException.class)
	public ResponseResult parameterBindExceptionHandler(ConstraintViolationException e) {
		Set<ConstraintViolation<?>> validateSet = e.getConstraintViolations();
		if (!CollectionUtils.isEmpty(validateSet)) {
			String messages = validateSet.stream()
				.map(ConstraintViolation::getMessage)
				.reduce((m1, m2) -> m1 + "；" + m2)
				.orElse("参数输入有误！");
			return new ResponseResult(ResultEnum.PARAMETER_ERROR.getCode(), messages);

		}
		return new ResponseResult(ResultEnum.PARAMETER_ERROR);
	}

	/**
	 * 自定义参数错误异常处理器
	 *
	 * @param e 自定义参数
	 * @return ResponseInfo
	 */
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ExceptionHandler({ParamErrorException.class})
	public ResponseResult paramExceptionHandler(ParamErrorException e) {
		log.error("", e);
		// 判断异常中是否有错误信息，如果存在就使用异常中的消息，否则使用默认消息
		if (!StringUtils.isEmpty(e.getMessage())) {
			return new ResponseResult(ResultEnum.PARAMETER_ERROR.getCode(), e.getMessage());
		}
		return new ResponseResult(ResultEnum.PARAMETER_ERROR);
	}

	/**
	 * 其他异常
	 *
	 * @param e
	 * @return
	 */
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ExceptionHandler({Exception.class})
	public ResponseResult otherExceptionHandler(Exception e) {
		log.error("其他异常", e);
		// 判断异常中是否有错误信息，如果存在就使用异常中的消息，否则使用默认消息
		if (!StringUtils.isEmpty(e.getMessage())) {
			return new ResponseResult(ResultEnum.UNKNOWN_ERROR.getCode(), e.getMessage());
		}
		return new ResponseResult(ResultEnum.UNKNOWN_ERROR);
	}

}